package com.huang.auth.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huang.auth.constants.AuthConstants;
import com.huang.auth.constants.CommonLoginWrapperConstant;
import com.huang.auth.constants.UserRoleWrapperConstant;
import com.huang.auth.constants.UserWrapperConstant;
import com.huang.auth.converter.AuthUserInfoVOConverter;
import com.huang.auth.dao.AuthUserDao;
import com.huang.auth.dao.AuthUserRoleDao;
import com.huang.auth.dao.CommonLoginLogDao;
import com.huang.auth.entity.po.*;
import com.huang.auth.entity.request.*;
import com.huang.auth.entity.to.LoginTO;
import com.huang.auth.entity.to.TokenInfoTO;
import com.huang.auth.entity.vo.AuthUserInfoVO;
import com.huang.auth.entity.vo.RoutesVO;
import com.huang.auth.service.AuthUserService;
import com.huang.auth.utils.MenuUtils;
import com.huang.entity.request.RPage;
import com.huang.utils.JWTTokenUtil;
import com.huang.utils.ObjectIdUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户(AuthUser)表服务实现类
 *
 * @author HuangShen
 * @since 2021-09-21 17:13:58
 */
@Service("authUserService")
@RefreshScope
public class AuthUserServiceImpl extends ServiceImpl<AuthUserDao, AuthUserPO> implements AuthUserService {

    private static final Logger logger = LoggerFactory.getLogger(AuthUserServiceImpl.class);
    @Resource
    private AuthUserDao authUserDao;

    @Resource
    private AuthUserRoleDao authUserRoleDao;

    @Resource
    private CommonLoginLogDao commonLoginLogDao;


    @Resource
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Value("${com.huang.auth.jwt.secret:45112879655224447755513037744120}")
    private String JWTSecret;
    @Value("${com.huang.auth.jwt.expires:3}")
    private Long tokenExpiresIn;

    /**
     * 登录
     *
     * @param loginRequest loginRequest
     * @return LoginTO 0:账号不存在 1:密码错误 2:成功 3:账号未启用
     */
    @Override
    public LoginTO login(LoginRequest loginRequest) {
        LoginTO loginTO = new LoginTO();
        AuthUserPO authUserPO = this.selectOneAuthPOByAccount(loginRequest.getUsername());

        // 用户不存在
        if (Objects.isNull(authUserPO)) {
            loginTO.setMessage("账号不存在");
            loginTO.setStatus(AuthConstants.NO_ACCOUNT_CODE);
            return loginTO;
        }

        // 登录日志信息配置
        CommonLoginLogPO commonLoginLogPO = new CommonLoginLogPO();
        commonLoginLogPO.setAccount(authUserPO.getAccount());
        commonLoginLogPO.setUserId(authUserPO.getId());
        commonLoginLogPO.setUserName(authUserPO.getName());
        commonLoginLogPO.setRequestIp(loginRequest.getIp());
        commonLoginLogPO.setLoginDate(new Date());
        commonLoginLogPO.setLocation("");
        commonLoginLogPO.setOperatingSystem(loginRequest.getOs());

        // 比对密码
        if (!bCryptPasswordEncoder.matches(loginRequest.getPassword(), authUserPO.getPassword())) {
            // 密码错误 记录登录日志
            commonLoginLogPO.setDescription("密码输入错误");
            this.commonLoginLogDao.insert(commonLoginLogPO);
            loginTO.setMessage("密码错误");
            loginTO.setStatus(AuthConstants.PASSWORD_ERROR_CODE);
            return loginTO;
        }

        // 判断用户是否启用
        if (!authUserPO.getStatus()) {
            // 账号未启用 记录登录日志
            commonLoginLogPO.setDescription("账号未启用 进行登录");
            this.commonLoginLogDao.insert(commonLoginLogPO);
            loginTO.setMessage("账号未启用");
            loginTO.setStatus(AuthConstants.DISABLE_ACCOUNT_CODE);
            return loginTO;
        }
        commonLoginLogPO.setDescription("登录成功");
        this.commonLoginLogDao.insert(commonLoginLogPO);
        // 获取角色信息
        List<AuthRolePO> authRolePOS = this.authUserDao.queryRoleById(authUserPO.getId());
        TokenInfoTO tokenInfoTO = new TokenInfoTO();
        tokenInfoTO.setUserAccount(authUserPO.getAccount());
        tokenInfoTO.setUserId(authUserPO.getId());
        authRolePOS.forEach(authRolePO -> tokenInfoTO.getRoleNames().add(authRolePO.getName()));
        // 生成token
        String token = JWTTokenUtil.creatToken(JSON.toJSONString(tokenInfoTO), JWTSecret, tokenExpiresIn);
        String refreshToken = JWTTokenUtil.creatToken(JSON.toJSONString(tokenInfoTO), JWTSecret, tokenExpiresIn + 2);
        loginTO.setToken(token);
        loginTO.setRefreshToken(refreshToken);
        loginTO.setExpires_in(tokenExpiresIn * 3600);
        loginTO.setStatus(AuthConstants.LOGIN_SUCCESS_CODE);
        return loginTO;
    }

    /**
     * 根据userId 获取用userInfo
     *
     * @param userId userId
     * @return AuthUserInfoVO
     */
    @Override
    public AuthUserInfoVO queryUserInfoById(Long userId) {
        AuthUserPO authUserPO = this.authUserDao.selectById(userId);
        if (Objects.isNull(authUserPO)) {
            logger.info("用户不存在 userId:{}", userId);
            return null;
        }
        List<AuthRolePO> authRolePOS = this.authUserDao.queryRoleById(userId);
        List<AuthResourcePO> authResourcePOS = this.authUserDao.queryResourceByUserId(userId);
        return AuthUserInfoVOConverter.builder(authUserPO, authRolePOS, authResourcePOS);

    }

    /**
     * 根据userId 获取 Menu
     *
     * @param userId userId
     * @return RoutesVO
     */
    @Override
    public Set<RoutesVO> queryMenuByUserId(String userId) {
        List<AuthMenuPO> authMenuPOS = this.authUserDao.queryMenuByUserId(userId);
        if (Objects.isNull(authMenuPOS)) {
            logger.info("查询菜单为空 userId:{}", userId);
            return null;
        }
        TreeSet<RoutesVO> routesVOS = new TreeSet<>();
        Map<Integer, List<AuthMenuPO>> authMenuByLeve = authMenuPOS.stream().collect(Collectors.groupingBy(AuthMenuPO::getLevel));
        Map<Long, List<AuthMenuPO>> authMenuByParentId = authMenuPOS.stream().collect(Collectors.groupingBy(AuthMenuPO::getParentId));


        authMenuByLeve.get(1).forEach(authMenuPO -> {
            RoutesVO routesVO = new RoutesVO();
            RoutesVO.RouteMeta meta = routesVO.new RouteMeta();
            meta.setBadge(authMenuPO.getBadge());
            meta.setDot(authMenuPO.getDot());
            meta.setIcon(authMenuPO.getIcon());
            meta.setTitle(authMenuPO.getTitle());
            routesVO.setSortValue(authMenuPO.getSortValue());
            routesVO.setMeta(meta);
            routesVO.setComponent(authMenuPO.getComponent());
            routesVO.setName(authMenuPO.getName());
            routesVO.setPath(authMenuPO.getPath());
            MenuUtils.creatRoutesChildren(authMenuByParentId, routesVO, authMenuPO);
            routesVOS.add(routesVO);
        });


        return routesVOS;
    }

    @Override
    public boolean changeUserStatus(String account, Long operatorId) {
        AuthUserPO authUserPO = this.selectOneAuthPOByAccount(account);
        if (Objects.isNull(authUserPO)) {
            return false;
        }
        authUserPO.setStatus(!authUserPO.getStatus());
        return this.authUserDao.updateById(authUserPO) > 0;
    }

    @Override
    public boolean delUser(String account, Long operatorId) {
        QueryWrapper<AuthUserPO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(UserWrapperConstant.ACCOUNT, account);
        return (this.authUserDao.delete(queryWrapper) > 0);
    }

    /**
     * 根据用户账号查询 可以访问的资源
     *
     * @param userId 账号
     * @return list
     */
    @Override
    public List<AuthResourcePO> queryResourceByUserId(Long userId) {
        return this.authUserDao.queryResourceByUserId(userId);
    }

    /**
     * 添加用户
     *
     * @param authUserAddRequest 添加用户vo
     * @param createUser         createUser
     * @return boolean
     */
    @Override
    @Transactional
    public boolean userAdd(AuthUserAddRequest authUserAddRequest, Long createUser) {

        if (Objects.nonNull(this.selectOneAuthPOByAccount(authUserAddRequest.getAccount()))) {
            // 账户已存在
            return false;
        }
        AuthUserPO authUserPO = new AuthUserPO();
        authUserPO.setId(ObjectIdUtils.getInstance().nextId());
        authUserPO.setAccount(authUserAddRequest.getAccount());
        authUserPO.setMobile(authUserAddRequest.getMobile());
        authUserPO.setName(authUserAddRequest.getName());
        authUserPO.setWorkDescribe(authUserAddRequest.getWorkDescribe());
        authUserPO.setEmail(authUserAddRequest.getEmail());
        authUserPO.setPassword(bCryptPasswordEncoder.encode(authUserAddRequest.getMobile().substring(5)));
        authUserPO.setCreateUser(createUser);
        int userAdd = this.authUserDao.insert(authUserPO);
        if (CollectionUtils.isEmpty(authUserAddRequest.getRoleIds())) {
            return userAdd > 0;
        }
        List<AuthUserRolePO> authUserRolePOS = authUserAddRequest.getRoleIds().stream()
                .map(roleId -> {
                    AuthUserRolePO authUserRolePO = new AuthUserRolePO();
                    authUserRolePO.setUserId(authUserPO.getId());
                    authUserRolePO.setCreateUser(authUserRolePO.getCreateUser());
                    authUserRolePO.setRoleId(Long.parseLong(roleId));
                    authUserRolePO.setId(ObjectIdUtils.getInstance().nextId());
                    return authUserRolePO;
                }).collect(Collectors.toList());
        int userRoleAdd = this.authUserRoleDao.insertBatch(authUserRolePOS);
        return userAdd > 0 && userRoleAdd > 0;

    }


    /**
     * 根据账号获取  AuthUserPO
     *
     * @param account 账号
     * @return AuthUserPO
     */
    @Override
    public AuthUserPO selectOneAuthPOByAccount(String account) {
        QueryWrapper<AuthUserPO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(UserWrapperConstant.ACCOUNT, account);
        return this.authUserDao.selectOne(queryWrapper);
    }

    @Override
    public boolean resPassword(String account, Long operatorId) {
        AuthUserPO authUserPO = this.selectOneAuthPOByAccount(account);
        if (Objects.isNull(authUserPO)) {
            logger.info("账号不存在 account:{}", account);
            return false;
        }
        // 重置密码 重置为手机号后4位
        String password = StringUtils.substring(authUserPO.getMobile(), 5);
        authUserPO.setPassword(bCryptPasswordEncoder.encode(password));
        return this.authUserDao.updateById(authUserPO) > 0;
    }

    @Override
    public boolean changePassword(PasswordChangeRequest passwordChangeRequest) {
        AuthUserPO authUserPO = this.selectOneAuthPOByAccount(passwordChangeRequest.getAccount());
        if (Objects.isNull(authUserPO)) {
            logger.info("账号不存在 account:{}", passwordChangeRequest.getAccount());
            return false;
        }
        authUserPO.setPassword(bCryptPasswordEncoder.encode(passwordChangeRequest.getPassword()));
        return this.authUserDao.updateById(authUserPO) > 0;
    }

    @Override
    @Transactional
    public boolean userEdit(AuthUserEditRequest authUserEditRequest) {
        AuthUserPO authUserPO = this.selectOneAuthPOByAccount(authUserEditRequest.getAccount());
        if (Objects.isNull(authUserPO)) {
            // 账户不存在
            return false;
        }
        authUserPO.setMobile(authUserEditRequest.getMobile());
        authUserPO.setName(authUserEditRequest.getName());
        authUserPO.setEmail(authUserEditRequest.getEmail());
        authUserPO.setWorkDescribe(authUserEditRequest.getWorkDescribe());
        authUserPO.setUpdateUser(Long.valueOf(authUserEditRequest.getUpdateUser()));
        int userEdit = this.authUserDao.updateById(authUserPO);

        List<String> resRoleIds = authUserEditRequest.getRoleIds();
        if (CollectionUtils.isEmpty(resRoleIds)) {
            QueryWrapper<AuthUserRolePO> delWrapper = new QueryWrapper<>();
            delWrapper.eq(UserRoleWrapperConstant.USER_ID, authUserPO.getId());
            this.authUserRoleDao.delete(delWrapper);
            return userEdit > 0;
        }
        QueryWrapper<AuthUserRolePO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(UserRoleWrapperConstant.USER_ID, authUserPO.getId());
        queryWrapper.select(UserRoleWrapperConstant.ROLE_ID, UserRoleWrapperConstant.USER_ID, UserRoleWrapperConstant.ID);
        List<Long> roleIds = this.authUserRoleDao.selectList(queryWrapper).stream().map(AuthUserRolePO::getRoleId).collect(Collectors.toList());
        // 找出新增的角色id
        List<AuthUserRolePO> newAuthUserRoles = resRoleIds.stream().filter(roleId -> !roleIds.contains(Long.parseLong(roleId))).map(roleId -> {
            AuthUserRolePO authUserRolePO = new AuthUserRolePO();
            authUserRolePO.setId(ObjectIdUtils.getInstance().nextId());
            authUserRolePO.setUserId(authUserPO.getId());
            authUserRolePO.setCreateUser(Long.getLong(authUserEditRequest.getUpdateUser()));
            authUserRolePO.setRoleId(Long.parseLong(roleId));
            authUserRolePO.setCreateTime(new Date());
            return authUserRolePO;
        }).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(newAuthUserRoles)) {

            return userEdit > 0;
        }
        int roleAdd = this.authUserRoleDao.insertBatch(newAuthUserRoles);
        // 过滤出需要删除的roleId
        List<Long> roleIdDel = roleIds.stream().filter(aLong -> !resRoleIds.contains(aLong.toString())).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(roleIdDel)) {
            return userEdit > 0 && roleAdd > 0;
        }
        QueryWrapper<AuthUserRolePO> delWrapper = new QueryWrapper<>();
        delWrapper.eq(UserRoleWrapperConstant.USER_ID, authUserPO.getId());
        delWrapper.in(UserRoleWrapperConstant.ROLE_ID, roleIdDel);
        int roleDel = this.authUserRoleDao.delete(delWrapper);
        return userEdit > 0 && roleAdd > 0 && roleDel > 0;
    }

    /**
     * 查询登陆日志
     */
    @Override
    public Page<CommonLoginLogPO> loginLogs(String userId, RPage rPage) {
        QueryWrapper<CommonLoginLogPO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(CommonLoginWrapperConstant.USER_ID, userId);
        queryWrapper.orderByDesc(CommonLoginWrapperConstant.CREATE_TIME);
        Page<CommonLoginLogPO> page = new Page<>();
        page.setSize(rPage.getPageSize());
        page.setCurrent(rPage.getPageNum());
        return this.commonLoginLogDao.selectPage(page, queryWrapper);
    }

    @Override
    public Page<AuthUserPO> userListByPage(AuthUserQueryRequest authUserQueryRequest) {
        Page<AuthUserPO> page = new Page<>();
        page.setCurrent(authUserQueryRequest.getPage().getPageNum());
        page.setSize(authUserQueryRequest.getPage().getPageSize());
        QueryWrapper<AuthUserPO> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotEmpty(authUserQueryRequest.getAccount())) {
            queryWrapper.eq(UserWrapperConstant.ACCOUNT, authUserQueryRequest.getAccount());
        }
        if (StringUtils.isNotEmpty(authUserQueryRequest.getName())) {
            queryWrapper.like(UserWrapperConstant.NAME, authUserQueryRequest.getName());
        }
        if (StringUtils.isNotEmpty(authUserQueryRequest.getEmail())) {
            queryWrapper.eq(UserWrapperConstant.EMAIL, authUserQueryRequest.getEmail());
        }
        if (StringUtils.isNotEmpty(authUserQueryRequest.getSex())) {
            queryWrapper.eq(UserWrapperConstant.SEX, authUserQueryRequest.getSex());
        }
        if (Objects.nonNull(authUserQueryRequest.getStatus())) {
            queryWrapper.eq(UserWrapperConstant.STATUS, authUserQueryRequest.getStatus());
        }
        if (StringUtils.isNotEmpty(authUserQueryRequest.getMobile())) {
            queryWrapper.eq(UserWrapperConstant.MOBILE, authUserQueryRequest.getMobile());
        }

        return this.authUserDao.selectPage(page, queryWrapper);

    }

    @Override
    public List<String> queryRoleIdByUserId(String userId) {
        QueryWrapper<AuthUserRolePO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(UserRoleWrapperConstant.USER_ID, userId);
        return this.authUserRoleDao.selectList(queryWrapper).stream()
                .map(authUserRolePO -> authUserRolePO.getRoleId().toString())
                .collect(Collectors.toList());
    }

}

